Namespace Component

    ''' <summary>
    ''' MultiSelect supported treeview
    ''' </summary>
    ''' <remarks></remarks>
    Public Class MultiSelectTreeView
        Inherits Windows.Forms.TreeView

        Private Declare Function GetKeyState Lib "user32" Alias "GetKeyState" (ByVal nVirtKey As Integer) As Integer

        Private Const VK_CONTROL As Integer = &H11
        Private Const VK_SHIFT As Integer = &H10

        Private m_MutliSelect As Boolean
        Private m_SelBackColor, m_SelForeColor As Drawing.Color
        Private m_SelNodes As ArrayList
        Private m_AlterKeyStatus As AlterKeyStatus
        Private m_ndPerSelect As Windows.Forms.TreeNode

        Private Enum AlterKeyStatus
            None = 0
            Ctrl = 1
            Shift = 2
        End Enum

        ''' <summary>
        ''' The treeview support mutliselect.
        ''' </summary>
        ''' <value></value>
        ''' <returns>Support mutliselect.</returns>
        ''' <remarks></remarks>
        Public Property MutliSelect() As Boolean
            Get
                Return Me.m_MutliSelect
            End Get
            Set(ByVal Value As Boolean)
                Me.m_MutliSelect = Value
            End Set
        End Property

        ''' <summary>
        ''' Backcolor of multiselected node.
        ''' </summary>
        ''' <value></value>
        ''' <returns>Backcolor of multiselected node.</returns>
        ''' <remarks></remarks>
        Public Property SelectedBackColor() As Drawing.Color
            Get
                Return Me.m_SelBackColor
            End Get
            Set(ByVal Value As Drawing.Color)
                Me.m_SelBackColor = Value
            End Set
        End Property

        ''' <summary>
        ''' Forecolor of multiselected node.
        ''' </summary>
        ''' <value></value>
        ''' <returns>Forecolor of multiselected node.</returns>
        ''' <remarks></remarks>
        Public Property SelectedForeColor() As Drawing.Color
            Get
                Return Me.m_SelForeColor
            End Get
            Set(ByVal Value As Drawing.Color)
                Me.m_SelForeColor = Value
            End Set
        End Property

        ''' <summary>
        ''' Selected nodes.
        ''' </summary>
        ''' <value></value>
        ''' <returns>Selected nodes.</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property SelectedNodes() As Windows.Forms.TreeNode()
            Get
                Return Me.m_SelNodes.ToArray(GetType(Windows.Forms.TreeNode))
            End Get
        End Property

        ''' <summary>
        ''' Create instance.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub New()
            MyBase.New()

            Me.m_MutliSelect = True
            Me.m_SelBackColor = Drawing.SystemColors.ActiveCaption
            Me.m_SelForeColor = Drawing.Color.White
            Me.m_SelNodes = New ArrayList
        End Sub

        ''' <summary>
        ''' Add the selected nodes and set the style (used in SHIFT).
        ''' </summary>
        ''' <param name="nd">Current selected node.</param>
        ''' <remarks></remarks>
        Private Sub FillSelectedNode(ByVal nd As Windows.Forms.TreeNode)
            Dim _nd As Windows.Forms.TreeNode
            Me.m_SelNodes.Clear()
            If (Not Me.m_ndPerSelect Is Nothing) AndAlso (Not Me.m_ndPerSelect Is nd) AndAlso (Me.m_ndPerSelect.Parent Is nd.Parent) Then
                Me.ClearSelectStyle(Me.Nodes(0))
                If nd.Index > Me.m_ndPerSelect.Index Then
                    Me.m_SelNodes.Add(Me.m_ndPerSelect)
                    _nd = Me.m_ndPerSelect.NextVisibleNode
                    While Not _nd Is Nothing AndAlso Not _nd Is nd
                        Me.m_SelNodes.Add(_nd)
                        _nd = _nd.NextVisibleNode
                    End While
                    Me.m_SelNodes.Add(nd)
                Else
                    Me.m_SelNodes.Add(nd)
                    _nd = nd.NextVisibleNode
                    While Not _nd Is Nothing AndAlso Not Me.m_ndPerSelect Is nd
                        Me.m_SelNodes.Add(_nd)
                        _nd = _nd.NextVisibleNode
                    End While
                    Me.m_SelNodes.Add(Me.m_ndPerSelect)
                End If
                For Each _nd In Me.m_SelNodes
                    _nd.BackColor = Me.m_SelBackColor
                    _nd.ForeColor = Me.m_SelForeColor
                Next
            Else
                Me.m_SelNodes.Add(nd)
            End If
        End Sub

        ''' <summary>
        ''' Clear the node and its child nodes' selected style.
        ''' </summary>
        ''' <param name="nd">Parent node.</param>
        ''' <remarks></remarks>
        Private Sub ClearSelectStyle(ByVal nd As Windows.Forms.TreeNode)
            Dim _nd As Windows.Forms.TreeNode
            nd.BackColor = Drawing.Color.Empty
            nd.ForeColor = Drawing.Color.Black
            For Each _nd In nd.Nodes
                Me.ClearSelectStyle(_nd)
            Next
        End Sub

        ''' <summary>
        ''' Remove the node from selected nodes list (used in CTRL).
        ''' </summary>
        ''' <param name="nd">The node to be removed.</param>
        ''' <remarks></remarks>
        Private Sub RemoveSelectNode(ByVal nd As Windows.Forms.TreeNode)
            If Me.m_SelNodes.Contains(nd) = True Then
                Me.m_SelNodes.Remove(nd)
                nd.BackColor = (New Windows.Forms.TreeNode).BackColor
            End If
        End Sub

        ''' <summary>
        ''' Append the node in selected nodes list (used in CTRL).
        ''' </summary>
        ''' <param name="nd">The node to be added.</param>
        ''' <remarks></remarks>
        Private Sub AppendSelectedNode(ByVal nd As Windows.Forms.TreeNode)
            Dim _nd As Windows.Forms.TreeNode
            Dim blnExist As Boolean = False
            For Each _nd In Me.m_SelNodes
                If nd Is _nd Then
                    Me.RemoveSelectNode(nd)
                    blnExist = True
                    Exit For
                End If
            Next
            If blnExist = False Then
                Me.m_SelNodes.Add(nd)
                For Each _nd In Me.m_SelNodes
                    _nd.BackColor = Me.m_SelBackColor
                    _nd.ForeColor = Me.m_SelForeColor
                Next
            End If
        End Sub

        ''' <summary>
        ''' Get the alter key when node is selected.
        ''' </summary>
        ''' <returns>The alter key status.</returns>
        ''' <remarks></remarks>
        Private Function GetAlterKeyStatus() As AlterKeyStatus
            Dim ret As AlterKeyStatus
            If Math.Abs(GetKeyState(VK_CONTROL)) >= 2 ^ 6 Then
                ret = AlterKeyStatus.Ctrl
            ElseIf Math.Abs(GetKeyState(VK_SHIFT)) >= 2 ^ 6 Then
                ret = AlterKeyStatus.Shift
            Else
                ret = AlterKeyStatus.None
            End If
            Return ret
        End Function

        ''' <summary>
        ''' After the node is selected, execute multi-select process.
        ''' </summary>
        ''' <param name="e"></param>
        ''' <remarks></remarks>
        Protected Overrides Sub OnAfterSelect(ByVal e As System.Windows.Forms.TreeViewEventArgs)
            MyBase.OnAfterSelect(e)
            If Me.m_MutliSelect = True Then
                If Not Me.SelectedNode Is Nothing Then
                    Select Case GetAlterKeyStatus()
                        Case AlterKeyStatus.Ctrl
                            Me.AppendSelectedNode(Me.SelectedNode)
                        Case Else
                            Me.ClearSelectStyle(Me.Nodes(0))
                            Me.m_SelNodes.Clear()
                            Me.m_SelNodes.Add(Me.SelectedNode)
                    End Select
                End If
                Me.m_ndPerSelect = e.Node
            Else
                Me.m_SelNodes.Clear()
                Me.m_SelNodes.Add(e.Node)
            End If
        End Sub

        ''' <summary>
        ''' Clear selected nodes list and its style.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub ClearSelectedNodes()
            Me.ClearSelectStyle(Me.Nodes(0))
            Me.m_SelNodes.Clear()
        End Sub

    End Class

End Namespace
